home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 November / Macworld (1999-11).dmg / Updaters / WhiteCap 3.0.4 / WhiteCap Source.sit / WhiteCap Source / SJ SDK / Visual Plug-in White Paper < prev    next >
Text File  |  1999-07-20  |  10KB  |  118 lines

  1. Writing Visualization Plug-ins for SoundJam
  2. (Version 1.0.1, July 20, 1999, Mike Wright <darwin@mbay.net)
  3.  
  4. SoundJam can make use of several types of plug-ins. One of these is the "visualization" plug-in. A visualization plug-in is provided with a window in which it can draw a graphic that reacts to the music being played by SoundJam. It can also modify the music itself. A visualization plug-in can perform either or both of these tasks.
  5.  
  6. This document explains how a visualization plug-in should interact with SoundJam.
  7.  
  8. Linker Entry Points
  9. The Main entry point for the linker should be "main" (without the quotes, of course). The others (Initalization and Termination) should be blank.
  10.  
  11. The Framework Sample Code
  12.  
  13. A sample visualization plug-in, Visual Framework Plug-in, is provided, along with source code. You can use this code as the starting point for your own visualization plug-in. This code will be referred to in the following explanations. It includes the following files:
  14.  
  15. SoundJam Plug-in API files:
  16.     SoundJamAPI.c - functions for calling SoundJam's internal functions
  17.     SoundJamAPI.h - general SoundJam defines, typedefs, and functin prototypes
  18.     VisualPlugin.h - defines and typedefs specific to visualization plug-ins
  19.  
  20. Framework-specific files:
  21.     VisFramework.h - defines, typedefs, and function prototypes
  22.     VisFrameworkMain.c - functions called by SoundJam
  23.     VisFrameworkHandlers.c - handler functions for SoundJam messages
  24.     VisFrameworkRoutines.c - routines called by various handler functions
  25.  
  26. Each function has a fairly detailed header that explains what it should do, what the fields of the messages are, and things to watch for. After reading the following general explanation, we suggest that you look at the various functions in the VisFrameworkMain.c and VisFrameworkHandlers.c files.
  27.  
  28. Registering the Plug-in with SoundJam
  29.  
  30. When SoundJam starts up, it looks in the Plugins folder for files with a creator of SJAM and a type of PLUG. It then loads each plug-in in turn, sending a message to initiate registration of the plug-in with SoundJam.
  31.  
  32. The first time SoundJam calls the plug-in, it passes the following parameters to main():
  33.  
  34.     messageType    - kind of message contained in the messageInfo struct
  35.                              If this is kPluginInitializeMessage (SoundJamAPI.h), we
  36.                              register the plug-in by filling in a variable of type
  37.                              PlayerMessageInfo (SoundJamAPI.h) and passing it back to
  38.                              SoundJam by a call to the PlayerRegisterVisualPlugin()
  39.                              function (SoundJamAPI.c).
  40.  
  41.     messageInfo    -    pointer to a PluginMessageInfo struct (SoundJamAPI.h)
  42.                              This will contain a struct of type PluginInitMessage
  43.                              (SoundJamAPI.h). This contains input fields for the version
  44.                                                          of SoundJam, along with an appCookie and a playerProc
  45.                              that are passed back to SoundJam in the call to the
  46.                              PlayerRegisterVisualPlugin() function. The appCookie
  47.                              field is used to identify the plug-in, and the playerProc
  48.                              field contains a pointer to the plug-in in memory.
  49.                              The PluginInitMessage struct also contains output
  50.                              fields for options, and a refcon for storing the plug-in's
  51.                              own global data.
  52.  
  53.     refcon                            -    stored plug-in global data
  54.  
  55. The main() function is expected to return a value of type OSStatus, so it should look like this:
  56. OSStatus main (OSType messageType,PluginMessageInfo *messageInfo,void *refcon). See the main() function in VisFrameworkMain.c for an example of how to register a plug-in with SoundJam. Note that not all the fields of every struct are used at this time.
  57.  
  58. The structure of the main() function in VisFrameworkMain.c shouldn't require any changes to adapt it to any new visual plug-in. Just change the actual data being plugged into the fields of playerMessageInfo.u.registerVisualPluginMessage to reflect the requirements of your plug-in.
  59.  
  60. How SoundJam Calls the Plug-in
  61.  
  62. The .handler field of the PlayerMessageInfo struct is used to pass a pointer to the plug-in function that will handle incoming messages from SoundJam. In the sample code, this function is called VisHandler(), and it can be found in VisFrameworkMain.c.  It should look like this:
  63.  
  64. OSStatus VisHandler (OSType messageType,VisualPluginMessageInfo *messageInfo,void *refcon)
  65.  
  66. The messageType and refcon params are the same as those for the main() function. The messageInfo param (VisualPlugin.h) contains fields that are specific to visual plugins. The VisualPluginMessageInfo struct is a union of a variety of structs. The messageType param is the key to which of these structs has been included. These structs and the corresponding messageType constants are all defined in VisualPlugin.h.
  67.  
  68. Like the main() function, the VisHandler() function is designed to work for any visualization plug-in with only minimal changes. It calls a different handler function for each of the possible SoundJam messages, based on the messageType param. If the messageInfo param is relevant to the particular message, it passes it to the handler; otherwise, it does not.
  69.  
  70. In the sample code, we have declared a struct of type VisHandlerData (VisFramework.h) to hold all of our global data. This is the data that we want to continue to have access to from one call to the next. The VisHandler() function contains a VisHandlerData pointer which is set to the  refcon param. This pointer, which is passed to each of the handler functions, will be nil the first time the VisHandler() function is called by SoundJam, so the HandleInitializeMessage() function (VisFrameworkHandlers.c) must initialize it. Then, each message handler will fill in any relevant fields. You should modify the VisHandlerData struct by adding fields to contain the data that will be needed by your plug-in.
  71.  
  72. Handling Messages from SoundJam
  73.  
  74. SoundJam will send a variety of messages to your plug-in. Some, like the kVisualPluginInitializeMessage and kVisualPluginCleanupMessage messages will tell the plug-in that it is time to start up or to shut down. Others will tell it that a track has started playing, stopped playing, paused, etc. There is also a message for passing mouse click and keystroke events that may be handled by the plug-in. The details of each message can be understood by looking first at the VisHandler() function in VisFrameworkMain.c and then at the individual handler functions in VisFrameworkHandlers.c, all of which are pretty well commented.
  75.  
  76. Drawing in Response to the Sound Data
  77.  
  78. When it's time for the visual plug-in to actually do some drawing, SoundJam will send a messageType of kVisualPluginRenderMessage, and messageInfo will contain a VisualPluginRenderMessage struct, which consists of a timeStamp field and a renderData field. The renderData field is a struct of type RenderVisualData, which has four fields (see VisualPlugin.h):
  79.  
  80. waveform data: This is raw sound data.
  81.   UInt8  numWaveformChannels - number of waveform channels
  82.   UInt8  waveformData[2][512] - two channels, each with 512 bytes of waveform data
  83.     
  84. spectrum data: This is sound data processed into frequency data.
  85.     UInt8  numSpectrumChannels - number of spectrum channels
  86.     UInt8  spectrumData[2][512] - two channels, each with 512 bytes of spectrum data
  87.  
  88. The Visual Framework Plug-in sample plug-in uses only one channel of spectrum data. It uses this data to create several simple displays.
  89.  
  90. Modifying the Sound Data
  91.  
  92. In order to receive sound samples, you will have to set the kVisualWantsToProcessSamples option in main() (VisFrameworkMain.c). Then SoundJam will periodically send you a processSamples message containing VisualPluginProcessSamplesMessage struct (VisualPlugin.h). This struct includes a pointer to a sampleBuffer. This is the sound data that has not yet been played, so whatever is done to it will be heard when the sound is played.
  93.  
  94. The User Interface
  95.  
  96. Even if your plugin does not create a visual display, it should use the plug-in window to provide a user interface. SoundJam will pass mouse click and keystroke events that occur in the plug-in window to the plug-in. The sample plug-in includes a pop-up menu that is brought up in response to a click in the plug-in window (VisFrameworkRoutines.c).
  97.  
  98. Saving Preferences
  99.  
  100. Plug-ins can have SoundJam save and restore preference data via the PlayerSetPluginData() and PlayerGetPluginData() functions (SoundJamAPI.c).
  101.  
  102. The Visual Framework Plug-in sample plug-in calls PlayerSetPluginData() to save the color mode and drawing mode that are in effect when it shuts down, and restores them the next time it starts up by calling PlayerGetPluginData().
  103.  
  104. Error Handling
  105.  
  106. The plug-in should test for error conditions like any other application. If a fatal error occurs, it should be passed back down the line to the VisHandler() function, which will pass it back to SoundJam.
  107.  
  108. Things to Watch For
  109.  
  110. It is especially important that each plug-in should dispose of any memory that it allocates. This is especially true of drawing memory that is allocated each time the plug-in receives a messageType of kVisualPluginEnableMessage. A plug-in may be enabled and disabled many times, and failing to dispose of allocated memory after receiving a messageType of kVisualPluginDisableMessage could result in a severe memory leak. Of course, all allocated memory should be disposed of on receiving a messageType of kVisualPluginCleanupMessage.
  111.  
  112. It's also a good idea to use temporary memory whenever possible.
  113.  
  114. Drawing should, naturally, be done as efficiently as possible. If a drawing routine takes more time than is allocated to it, it can interfere with playing the music and can make it difficult--or even impossible--for the application to receive events. If your plug-in has a resizeable  window, be sure to test it with the window set as large as possible, since drawing a large image may take longer than drawing a small one.
  115.  
  116. Be sure to return an error other than noErr (e.g., paramErr) whenever you receive a keyDown event that you do not handle, in order to avoid swallowing keystrokes that SoundJam may react to.
  117.  
  118. Don't set the minimum window size too small for scroll bars to be drawn properly.